/*
 * Copyright 2016-2022 dromara.org.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.dromara.mendmix.common.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dromara.mendmix.common.GlobalConstants;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.NullNode;
import com.google.common.collect.ImmutableMap;


/**
 * xml工具类
 * @author jiangwei
 * 2025年5月11日
 */
public class XmlUtils {
	
	private static final String SINGE_NODE_ALIAS_NAME = "value";

	public static Map<String, String> xmlToTilingMap(String xml) {
		return xmlToTilingMap(xml, null);
	}
	
	public static Map<String, String> xmlToTilingMap(String xml,String selectNodeName) {
		try {
			Document document = DocumentHelper.parseText(xml);
			Element root = document.getRootElement();
			List<Element> elements = root.elements();
			Map<String, String> result = new LinkedHashMap<>();
			if(elements.isEmpty()) {
				result.put(root.getName(), root.getTextTrim());
			}else {
				String keyPrefix;
				for (Element element : elements) {
					keyPrefix = StringUtils.join(root.getName(),GlobalConstants.DOT,element.getName());
					xmlToTilingMap(element,keyPrefix,result,0);
				}
			}
			if(StringUtils.isNotBlank(selectNodeName)) {
				Map<String, String> subMap = new HashMap<>();
				result.forEach((k,v) -> {
					if(k.startsWith(selectNodeName)) {
						subMap.put(k.substring(selectNodeName.length() + 1), v);
					}
				});
				result = subMap;
			}
            return result;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		
	}
	
	public static JsonNode xmlToJsonNode(String xml,String selectNodeName,String...arrayNodeNames) {
		Map<String, Object> map = xmlToMap(xml, arrayNodeNames);
		if(map == null)return NullNode.getInstance();
		JsonNode jsonNode = JsonUtils.toJsonNode(JsonUtils.toJson(map));
		if(StringUtils.isNotBlank(selectNodeName)) {
			String[] fieldNames = StringUtils.split(selectNodeName, GlobalConstants.DOT);
			for (String fieldName : fieldNames) {
				jsonNode = jsonNode.get(fieldName);
			}
		}
		if(jsonNode == null) {
			jsonNode = NullNode.getInstance();
		}
		return jsonNode;
	}
	
	public static Map<String, Object> xmlToMap(String xml,String...arrayNodeNames) {
		try {
			Document document = DocumentHelper.parseText(xml);
			Element root = document.getRootElement();
			List<Element> elements = root.elements();
			List<String> arrayNodeNameList = new ArrayList<>();
			if(arrayNodeNames != null) {
				for (String name : arrayNodeNames) {
					if(name != null)arrayNodeNameList.add(name);
				}
			}
			Map<String, Object> result = new LinkedHashMap<>();
			if(elements.isEmpty()) {
				result.put(root.getName(), root.getTextTrim());
				result.put(XmlUtils.SINGE_NODE_ALIAS_NAME, root.getTextTrim());
			}else {
				for (Element element : elements) {
					xmlToMap(element,arrayNodeNameList,result);
				}
			}
			//
			if(elements.size() > 0) {
				return ImmutableMap.of(root.getName(), result);
			}
            return result;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	public static String jsonToXml(String json,String rootName) {
		JsonNode jsonNode = JsonUtils.toJsonNode(json);
		Document document = DocumentHelper.createDocument();
        Element root = document.addElement(rootName);
        jsonNodeToXmlElement(null, jsonNode, root);
		return root.asXML();
	}
	
	private static void jsonNodeToXmlElement(String fieldName,JsonNode jsonNode,Element parent) {
		if(jsonNode.isArray()) {
			ArrayNode arrayNode = (ArrayNode) jsonNode;
			for (JsonNode itemNode : arrayNode) {
				Element child = parent.addElement(fieldName);
				jsonNodeToXmlElement(fieldName, itemNode, child);
			}
        }else if(jsonNode.isObject()) {
        	Iterator<Entry<String, JsonNode>> iterator = jsonNode.fields();
        	while(iterator.hasNext()) {
				Entry<String, JsonNode> entry = iterator.next();
				String key = entry.getKey();
	            JsonNode subNode = entry.getValue();
	            if(subNode.isObject()) {
	            	 Element child = parent.addElement(key);
	            	 jsonNodeToXmlElement(key, subNode, child);
	            }else {
	            	jsonNodeToXmlElement(key, subNode, parent);
	            }
        	}
        }else {
        	Element child = parent.addElement(fieldName);
        	child.addText(jsonNode.asText());
        }
		
	}
 
	private static void xmlToMap(Element rootElement,List<String> arrayNodeNames,Map<String, Object> result) {
		List<Element> elements = rootElement.elements();
		final String rootNodeName = rootElement.getName();
		if(elements.isEmpty()) {
			result.put(rootNodeName, rootElement.getTextTrim());
		}else {
			Map<String, Object> subMap = new LinkedHashMap<>();
			List<Object> itemArray = null;
			if(arrayNodeNames.contains(rootNodeName)) {
				itemArray = new ArrayList<>(elements.size());
			}
			for (Element element : elements) {
				xmlToMap(element, arrayNodeNames, subMap);
				if(itemArray != null) {
					itemArray.add(subMap);
				}else {
					result.put(rootNodeName, subMap);
				}
			}
			if(itemArray != null) {
				result.put(rootNodeName, itemArray);
			}
		}
	}
	
	private static void xmlToTilingMap(Element rootElement,String keyPrefix,Map<String, String> result,int index) {
		List<Element> elements = rootElement.elements();
		if(elements.isEmpty()) {
			if(StringUtils.isNotBlank(keyPrefix) && !keyPrefix.endsWith(".")) {
				keyPrefix = keyPrefix + ".";
			}
			String key = StringUtils.trimToEmpty(keyPrefix) + rootElement.getName();
			result.put(key, rootElement.getTextTrim());
		}else {
			String _keyPrefix;
			index = 0;
			boolean isArray;
			for (Element element : elements) {
				isArray = rootElement.elements(element.getName()).size() > 1;
				if(element.elements().isEmpty()) {
					_keyPrefix = keyPrefix;
				}else {					
					_keyPrefix = StringUtils.isBlank(keyPrefix) ? element.getName() : keyPrefix + "." + element.getName();
				}
				if(isArray) {
					_keyPrefix = _keyPrefix +"["+index+"]";
				}
				xmlToTilingMap(element, _keyPrefix, result,index);
				index++;
			}
		}
	}
}
